/*
 * This file is part of ELCube.
 *
 * ELCube is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ELCube is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with ELCube.  If not, see <https://www.gnu.org/licenses/>.
 */
package cn.nkpro.elcube.docengine.service.impl;

import cn.nkpro.elcube.basic.TransactionSync;
import cn.nkpro.elcube.cloud.CloudConstants;
import cn.nkpro.elcube.cloud.NkCloudProperties;
import cn.nkpro.elcube.cloud.NkCloudSyncPayload;
import cn.nkpro.elcube.co.mq.NkMQManager;
import cn.nkpro.elcube.docengine.cloud.NkCloudDocEngineLock;
import cn.nkpro.elcube.docengine.cloud.NkCloudDocEngineSyncHelper;
import cn.nkpro.elcube.docengine.cloud.NkCloudSyncNotEnabledException;
import cn.nkpro.elcube.docengine.model.DocHV;
import cn.nkpro.elcube.exception.NkOperateNotAllowedCaution;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.support.TransactionSynchronization;

/**
 * 主数据单据处理器
 */
public abstract class NkDocMasterProcessor extends NkDocTransactionProcessor {

    @Autowired
    protected NkCloudProperties cloudProperties;
    @Autowired(required = false)
    protected NkCloudDocEngineLock docEngineLock;
    @Autowired(required = false)
    protected NkMQManager rabbitMQManager;


    /**
     * 单据更新逻辑有4种情况
     *
     *
     *
     *
     * @param identification 随机产生的新标识符，后续优化为doc json MD5
     */
    @Override
    public DocHV doUpdate(DocHV doc, DocHV original, String optSource, String identification) {

        /*
         * MDM模式 同步更新
         * 如果没有开启同步，则忽略
         */
        if(NkCloudDocEngineSyncHelper.isSyncUpdate() && cloudProperties.getMode() == NkCloudProperties.Mode.MDM){
            if(doc.getDef().getCloudSyncEnablePush()!=null && doc.getDef().getCloudSyncEnablePush()==1) {
                try{
                    return super.execDoUpdate(doc, original, optSource, identification);
                }finally {
                    DocHV finalDoc = doc;
                    TransactionSync.runAfterCompletionLast("同步单据到Slave", (state) -> {
                        if (state == TransactionSynchronization.STATUS_COMMITTED) {
                            docEngineLock.releaseLock(finalDoc.getDocId(), identification);
                            // 发送MQ消息到Slave router=$docType.cloudId
                            rabbitMQManager.publish(
                                    CloudConstants.EXCHANGE_NAME,
                                    "$" + finalDoc.getDocType() + "@" + cloudProperties.getId(),
                                    new NkCloudSyncPayload("docId", cloudProperties.getId(), finalDoc.getDocId()).toString()
                            );
                        }
                    });
                }
            }else{
                throw new NkCloudSyncNotEnabledException("云同步尚未启用");
            }
        }

        if(NkCloudDocEngineSyncHelper.isSyncUpdate() && cloudProperties.getMode() == NkCloudProperties.Mode.CLOUD){
            if(doc.getDef().getCloudSyncEnablePush()!=null && doc.getDef().getCloudSyncEnablePush()==1) {
                return super.execDoUpdate(doc, original, optSource, identification);
            }else{
                throw new NkCloudSyncNotEnabledException("云同步尚未启用");
            }
        }

        /*
         * MDM模式
         */
        if(cloudProperties.getMode() == NkCloudProperties.Mode.MDM){
            if(doc.getDef().getCloudSyncEnablePush()!=null && doc.getDef().getCloudSyncEnablePush()==1) {
                boolean lock = docEngineLock.obtainTemporaryLock(doc.getDocId(),doc.getIdentification(),identification);
                if(lock){
                    try{
                        // 修改updatedProducerId
                        doc.setUpdatedProducerId(cloudProperties.getId());
                        doc = super.execDoUpdate(doc, original, optSource, identification);
                        return doc;
                    }finally {
                        DocHV finalDoc = doc;
                        TransactionSync.runAfterCompletionLast("同步单据到Slave", (state) -> {
                            // 立即释放Cloud全局锁
                            docEngineLock.releaseLock(finalDoc.getDocId(), identification);
                            // 如果成功，同步MQ
                            if (state == TransactionSynchronization.STATUS_COMMITTED) {
                                // 发送MQ消息到Slave router=$docType.cloudId
                                rabbitMQManager.publish(
                                        CloudConstants.EXCHANGE_NAME,
                                        "$" + finalDoc.getDocType() + "@" + cloudProperties.getId(),
                                        new NkCloudSyncPayload("docId", cloudProperties.getId(), finalDoc.getDocId()).toString()
                                );
                            }
                        });
                    }
                }
                throw new NkOperateNotAllowedCaution("单据已被其他模块锁定，请稍候再试");
            }else{
                // 修改updatedProducerId
                doc.setUpdatedProducerId(cloudProperties.getId());
                doc = super.execDoUpdate(doc, original, optSource, identification);
                return doc;
            }
        }

        if(cloudProperties.getMode() == NkCloudProperties.Mode.CLOUD){

            // 修改updatedProducerId
            doc.setUpdatedProducerId(cloudProperties.getId());

            if(StringUtils.isBlank(cloudProperties.getMdmId())){
                return super.execDoUpdate(doc, original, optSource, identification);
            }else if(doc.getDef().getCloudSyncEnablePush()!=null && doc.getDef().getCloudSyncEnablePush()==1){
                // 向MDM获得临时锁 锁的内容即单据的 identification
                boolean lock = docEngineLock.obtainTemporaryLock(doc.getDocId(),doc.getIdentification(),identification);
                if(lock){
                    try{
                        doc = super.execDoUpdate(doc, original, optSource, identification);
                        return doc;
                    }finally {
                        DocHV finalDoc = doc;
                        TransactionSync.runAfterCompletionLast("同步单据到MDM", (state) -> {
                            if (state == TransactionSynchronization.STATUS_COMMITTED) {
                                docEngineLock.obtainPermanentLock(finalDoc.getDocId(), identification);
                                // 发送MQ消息到MDM
                                rabbitMQManager.publish(
                                        CloudConstants.EXCHANGE_NAME,
                                        CloudConstants.QUEUE_NAME + cloudProperties.getMdmId(),
                                        new NkCloudSyncPayload("doc", cloudProperties.getId(), finalDoc).toString()
                                );
                            } else {
                                docEngineLock.releaseLock(finalDoc.getDocId(), identification);
                            }
                        });
                    }
                }
                throw new NkOperateNotAllowedCaution("单据已被其他模块锁定，请稍候再试");
            }else{
                return super.execDoUpdate(doc, original, optSource, identification);
            }
        }

        return super.execDoUpdate(doc, original, optSource, identification);
    }
}
